{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**注：本文只介绍python3.6+** "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Windows调用外部程序\n",
    "\n",
    "可以使用win32process模块中的函数。如果想进一步控制进程，则可以使用ctype模块，直接调用kernel32.dll中的函数\n",
    "\n",
    "1. 使用os.system函数运行其他程序\n",
    "2. 使用ShellExecute函数运行其他程序\n",
    "3. 使用CreateProcess函数运行其他程序\n",
    "4. 使用ctypes调用kernel32.dll中的函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import win32api\n",
    "import win32process\n",
    "import win32event"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## os.system(command)\n",
    "\n",
    "\n",
    "说明：\n",
    "\n",
    "- 等待执行\n",
    "- 正常结束返回 0\n",
    "\n",
    "或者 os.popen(command), 详细讲解见下文"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 用法举例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "\n",
    "#  打开记事本程序\n",
    "os.system('notepad')\n",
    "# 关闭记事本后的返回值  0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 向记事本传递参数，打开python.txt文件\n",
    "os.system('notepad python.txt')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## win32api.ShellExecute(hwnd, op , file , params , dir , bShow )\n",
    "\n",
    "参数:\n",
    "- hwnd：父窗口的句柄，如果没有父窗口，则为0。\n",
    "- op：要进行的操作，为“open”、“print”或者为空。\n",
    "- file：要运行的程序，或者打开的脚本。\n",
    "- params：要向程序传递的参数，如果打开的为文件，则为空。\n",
    "- dir：程序初始化的目录。\n",
    "- bShow：是否显示窗口。\n",
    "\n",
    "说明：\n",
    "- 不用等待\n",
    "- 相当于在资源管理器中双击文件图标一样，系统会打开相应的应用程序执行操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 用法举例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import win32api"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "打开记事本程序，在后台运行，即显示记事本程序的窗口"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "42"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "win32api.ShellExecute(0, 'open', 'notepad', '', '', 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "打开记事本程序，在前台运行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "42"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "win32api.ShellExecute(0, 'open', 'notepad.exe', '', '', 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "向记事本传递参数，打开python.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "42"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "win32api.ShellExecute(0, 'open', 'notepad.exe', 'python.txt', '', 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在默认浏览器中打开http://www.python.org网站"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "42"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "win32api.ShellExecute(0, 'open', 'http://www.python.org', '', '', 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在默认的媒体播放器中播放E:\\song.wma"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "win32api.ShellExecute(0, 'open', 'E:\\\\song.wma', '', '', 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行位于E:\\book\\code目录中的MessageBox.py脚本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "win32api.ShellExecute(0, 'open', 'E:\\\\book\\\\code\\\\MessageBox.py', '', '', 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## win32process.CreateProcess 创建进程\n",
    "\n",
    "形式：\n",
    "\n",
    "`\n",
    "CreateProcess(appName, commandLine , processAttributes , threadAttributes , bInheritHandles ,dwCreationFlags , newEnvironment , currentDirectory , startupinfo )`\n",
    "\n",
    "参数：\n",
    "\n",
    "- appName：可执行的文件名。\n",
    "- commandLine：命令行参数。\n",
    "- processAttributes：进程安全属性，如果为None，则为默认的安全属性。\n",
    "- threadAttributes：线程安全属性，如果为None，则为默认的安全属性。\n",
    "- bInheritHandles：继承标志。\n",
    "- dwCreationFlags：创建标志。\n",
    "- newEnvironment：创建进程的环境变量。\n",
    "- currentDirectory：进程的当前目录。\n",
    "- startupinfo ：创建进程的属性。\n",
    "\n",
    "说明：\n",
    "- 不用等待\n",
    "- 可通过句柄控制终止、等待"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import win32process\n",
    "\n",
    "handle = win32process.CreateProcess('c:\\\\windows\\\\notepad.exe', '', None, None,\n",
    "                                    0, win32process.CREATE_NO_WINDOW, None,\n",
    "                                    None, win32process.STARTUPINFO())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 终止控制 win32process.TerminateProcess\n",
    "\n",
    "参数：\n",
    "- handle：要操作的进程句柄。\n",
    "- exitCode：进程退出代码。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "handle = win32process.CreateProcess('c:\\\\windows\\\\notepad.exe', '', None, None,\n",
    "                                    0, win32process.CREATE_NO_WINDOW, None,\n",
    "                                    None, win32process.STARTUPINFO())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "win32process.TerminateProcess(handle[0], 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 等待控制 win32event.WaitForSingleObject\n",
    "\n",
    "参数：\n",
    "- handle：要操作的进程句柄。\n",
    "- milliseconds：等待的时间，如果为1，则一直等待。\n",
    "\n",
    "返回值 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "handle = win32process.CreateProcess('c:\\\\windows\\\\notepad.exe', '', None, None,\n",
    "                                    0, win32process.CREATE_NO_WINDOW, None,\n",
    "                                    None, win32process.STARTUPINFO())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 等待进程结束， 等待时间 默认 -1（永远）\n",
    "win32event.WaitForSingleObject(handle[0], -1)\n",
    "# 进程结束的返回值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用ctypes调用kernel32.dll中的函数\n",
    "\n",
    "使用ctypes模块可以使Python调用由C语言编写的动态链接库，并向其传递参数。\n",
    "ctypes定义了C语言中的基本数据类型，并且可以实现C语言中的结构体和联合体。ctypes可以工作在Windows、Windows CE、Mac OS X、Linux、Solaris、FreeBSD、OpenBSD等平台上，基本上实现了跨平台"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在Windows下直接调用user32.dll中的MessageBoxA函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from ctypes import *\n",
    "\n",
    "user32 = windll.LoadLibrary('user32.dll')  # 加载动态链接库\n",
    "# 调用MessageBoxA函数.\n",
    "user32.MessageBoxA(0, b'Ctypes is cool!', 'Ctypes', 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# OS模块"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## os.system(command)\n",
    "\n",
    "- 等待执行\n",
    "- 有返回值\n",
    "- 输出流不可捕获\n",
    "\n",
    "\n",
    "[官方文档解释](https://docs.python.org/3.1/library/os.html#process-management)：\n",
    "\n",
    "翻译如下：\n",
    "\n",
    "在子shell中执行命令（参数：字符串）。这是通过**调用标准C函数系统（）来实现的，并且具有相同的限制。**\n",
    "\n",
    "改变 sys.stdin (,etc) 不会在执行此次命令中生效。如果command生成任何输出，它将被发送到解释器标准输出流。(即，所有输出信息都会显示到命令行中）\n",
    "\n",
    "在Unix上，返回值是以wait（）指定的格式编码的进程的退出状态。请注意，POSIX未指定C语言 system（）函数的返回值的含义，因此Python函数的返回值取决于系统。\n",
    "\n",
    "在Windows上，返回值是运行命令后系统shell返回的值。shell由Windows环境变量COMSPEC给出：它通常是cmd.exe，它返回命令运行的退出状态;在使用非本机shell的系统上，请参阅shell文档。\n",
    "\n",
    "**subprocess模块提供了更强大的工具来生成新进程并检查其结果;使用该模块比使用os.system更可取。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 用法举例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "#  打开记事本程序\n",
    "os.system('notepad')\n",
    "# 关闭记事本后的返回值  0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## os.popen\n",
    "- 不用等待，读取结果stdout需要等待\n",
    "- 返回管道（IO包装对象，类似文件对象），可 read()、readline() 等\n",
    "\n",
    "**本质是使用 subprocess.Popen 方法实现**\n",
    "\n",
    "**同样的，官方推荐使用 subprocess代替此方法。**\n",
    "\n",
    "底层实现如下：\n",
    "```python\n",
    "Signature: os.popen(cmd, mode='r', buffering=-1)\n",
    "Docstring: <no docstring>\n",
    "Source:   \n",
    "def popen(cmd, mode=\"r\", buffering=-1):\n",
    "    if not isinstance(cmd, str):\n",
    "        raise TypeError(\"invalid cmd type (%s, expected string)\" % type(cmd))\n",
    "    if mode not in (\"r\", \"w\"):\n",
    "        raise ValueError(\"invalid mode %r\" % mode)\n",
    "    if buffering == 0 or buffering is None:\n",
    "        raise ValueError(\"popen() does not support unbuffered streams\")\n",
    "    import subprocess, io\n",
    "    if mode == \"r\":\n",
    "        proc = subprocess.Popen(cmd,\n",
    "                                shell=True,\n",
    "                                stdout=subprocess.PIPE,\n",
    "                                bufsize=buffering)\n",
    "        return _wrap_close(io.TextIOWrapper(proc.stdout), proc)\n",
    "    else:\n",
    "        proc = subprocess.Popen(cmd,\n",
    "                                shell=True,\n",
    "                                stdin=subprocess.PIPE,\n",
    "                                bufsize=buffering)\n",
    "        return _wrap_close(io.TextIOWrapper(proc.stdin), proc)\n",
    "File:      d:\\softinstall\\anacoda3\\lib\\os.py\n",
    "        \n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.popen??"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 用法举例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<os._wrap_close at 0x2b6682f4080>"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "\n",
    "p = os.popen('ping www.baidu.com')\n",
    "p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "等待进程执行完毕后才可读出执行结果（阻塞）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "正在 Ping www.baidu.com [180.101.49.11] 具有 32 字节的数据:\n",
      "来自 180.101.49.11 的回复: 字节=32 时间=119ms TTL=53\n",
      "来自 180.101.49.11 的回复: 字节=32 时间=343ms TTL=53\n",
      "来自 180.101.49.11 的回复: 字节=32 时间=58ms TTL=53\n",
      "来自 180.101.49.11 的回复: 字节=32 时间=187ms TTL=53\n",
      "\n",
      "180.101.49.11 的 Ping 统计信息:\n",
      "    数据包: 已发送 = 4，已接收 = 4，丢失 = 0 (0% 丢失)，\n",
      "往返行程的估计时间(以毫秒为单位):\n",
      "    最短 = 58ms，最长 = 343ms，平均 = 176ms\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(p.read())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "''"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 输入一个错误命令\n",
    "p = os.popen('notepada')\n",
    "p.read()\n",
    "# 没有错误信息, 原因见上面实现代码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 说明：\n",
    "以上两个方法的 subprocess替代用法，见官方连接：\n",
    "\n",
    "[Replacing Older Functions with the subprocess Module](https://docs.python.org/3.1/library/subprocess.html#subprocess-replacements)\n",
    "\n",
    "---\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# subprocess 模块\n",
    "\n",
    "[官方文档](https://docs.python.org/3.1/library/subprocess.html#)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Popen--最基本的类\n",
    "\n",
    "说明：创建一个新的进程执行命令，返回一个实例，进行后续操作\n",
    "\n",
    "本质：\n",
    "\n",
    "在 POSIX，此类使用类似于 os.execvp() 的行为来执行子程序。\n",
    "在 Windows，此类使用了 Windows CreateProcess() 函数。\n",
    "\n",
    "- 非阻塞，可以wait()等待.\n",
    "- 支持多种参数和模式，方便控制和进程交互\n",
    "- 可捕获运行输出和返回状态码\n",
    "\n",
    "Popen原型是：\n",
    "\n",
    "```python\n",
    "Popen(args, bufsize=-1, executable=None,\n",
    "                 stdin=None, stdout=None, stderr=None,\n",
    "                 preexec_fn=None, close_fds=True,\n",
    "                 shell=False, cwd=None, env=None, universal_newlines=None,\n",
    "                 startupinfo=None, creationflags=0,\n",
    "                 restore_signals=True, start_new_session=False,\n",
    "                 pass_fds=(), *, encoding=None, errors=None, text=None)\n",
    "```\n",
    "参数：\n",
    "- `args`: 一个字符串命令，或者参数序列（最终还是转为一个字符串）。提供一个参数序列通常更好，它可以更小心地使用参数中的转义字符以及引用（例如允许文件名中的空格）。**如果传递一个简单的字符串，则 shell 参数（Linux下）必须为 True，或者用executable参数指定运行该命令的程序。**\n",
    "\n",
    "---\n",
    "\n",
    "参数既可以是string，也可以是list。\n",
    "\n",
    "对于参数是字符串，需要指定shell=True\n",
    "\n",
    "```python\n",
    "subprocess.Popen([“cat”,”test.txt”])\n",
    "subprocess.Popen(“cat test.txt”, shell=True)\n",
    "```\n",
    "\n",
    "linux下如果传入一个序列，则该序列的第一个元素默认当作执行后续参数的程序，比如：\n",
    "\n",
    ">cmd = [\"ssh\", 'root@192.168.1.2\"]\n",
    "\n",
    "则用ssh去执行后续参数\n",
    "\n",
    "---\n",
    "\n",
    "- `bufsize`：当创建stdin/stdout/stderr 管道文件时，为open() 方法提供buffering 参数z\n",
    "\n",
    "- `executable`：用于指定可执行程序。一般情况下我们通过args参数来设置所要运行的shell程序。如果将参数shell设为 True，executable将指定程序使用的shell。在windows平台下，默认的shell由COMSPEC环境变量来指定。（比如cmd.exe)\n",
    "\n",
    "- `stdin, stdout, stderr`分别表示程序的标准输入、输出、错误句柄。他们可以是:\n",
    "  - PIPE\n",
    "  - 文件描述符\n",
    "  - 文件对象，\n",
    "  - None，表示从父进程继承。\n",
    "  - `stderr` 还可以设置为`subprocess.STDOUT`。\n",
    "\n",
    "- `preexec_fn`：钩子函数，只在Unix平台下有效，用于指定一个**可执行对象**（callable object），它将在**子进程运行之前被调用**\n",
    "\n",
    "- `close_fds`：控制关闭或者继承文件描述符。在windows平台下，如果close_fds被设置为**True**，则新创建的子进程将**不会继承父进程的输入、输出、错误管道**。我们不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。\n",
    "\n",
    "- `shell`：如果设为`True`，unix下相当于`args`前面添加了 \"/bin/sh\" \"-c\" ，Windows下相当于添加了 \"cmd.exe /c\"\n",
    "\n",
    "- `cwd`：用于设置子进程的当前目录\n",
    "\n",
    "- `env`：字典类型，用于指定子进程的环境变量。如果env = None，子进程的环境变量将从父进程中继承\n",
    "\n",
    "- `universal_newlines`：控制统一换行符。不同操作系统下，文本的换行符是不一样的。如：windows下用’\\r\\n’表示换，而Linux下用 ‘\\n’。如果将此参数设置为True，Python统一把换行符处理为’\\n’。\n",
    "\n",
    "- `startupinfo`: 只在windows下用效，传递给底层CreateProcess()的结构体，用 于设置子进程的一些属性，如：主窗口的外观，进程的优先级等等\n",
    "\n",
    "- `creationflags`: 只在windows下用效, 传递给`CREATE_NEW_CONSOLE`创建自己的控制台窗口\n",
    "\n",
    "- `restore_signals`：POSIX only\n",
    "\n",
    "- `start_new_session`：POSIX only\n",
    "\n",
    "- `pass_fds`：POSIX only\n",
    "\n",
    "- `encoding` and `errors`: 文件对象`stdin、stdout、和stderr`的解码方式，以及解码的处理方式（比如：ignore、strict，参考`encode()`)\n",
    "\n",
    "- `text`：如果设为`True`，用给定的`encoding`参数解码stdin, stdout and stderr，如果`encoding`未指定，则使用系统默认编码。\n",
    "\n",
    "### 基本用法\n",
    "\n",
    "此例是在 windows 上执行一条错误命令notepad（打开记事本）。\n",
    "\n",
    "注意：\n",
    "如果不设置stderr=subprocess.PIPE，则获取不到错误信息\n",
    "\n",
    "communicate() 方法见下文。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "import subprocess\n",
    "\n",
    "p = subprocess.Popen('notepada',\n",
    "                     shell=True,\n",
    "                     stdout=subprocess.PIPE,\n",
    "                     stderr=subprocess.PIPE)\n",
    "\n",
    "# 注意解码Windows上默认 gbk，换行符为 \\r\\n\n",
    "stdoutdata, stderrdata = p.communicate()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "''"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "stdoutdata.decode('gbk')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"'notepada' 不是内部或外部命令，也不是可运行的程序\\r\\n或批处理文件。\\r\\n\""
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "stderrdata.decode('gbk')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__指定编码方式__ 或者 统一换行符参数都会将输出流编码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('', \"'notepada' 不是内部或外部命令，也不是可运行的程序\\n或批处理文件。\\n\")"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p = subprocess.Popen('notepada',\n",
    "                     shell=True,\n",
    "                     stdout=subprocess.PIPE,\n",
    "                     stderr=subprocess.PIPE, encoding='gbk')\n",
    "# p = subprocess.Popen('notepada',\n",
    "#                      shell=True,\n",
    "#                      stdout=subprocess.PIPE,\n",
    "#                      stderr=subprocess.PIPE, universal_newlines=True)\n",
    "\n",
    "stdoutdata, stderrdata = p.communicate()\n",
    "stdoutdata, stderrdata"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Popen的常用方法\n",
    "\n",
    "### Popen.poll() \n",
    "用于检查子进程是否已经结束。 \n",
    "\n",
    "- 不会等待进程\n",
    "- 进程未结束返回`None`\n",
    "- 进程结束返回returncode。\n",
    "\n",
    ">p.poll()\n",
    "\n",
    "---\n",
    "\n",
    "### Popen.wait(timeout=None)\n",
    "\n",
    "在规定时间内等待进程结束，设置timeout=None，则一直等待直到结束\n",
    "\n",
    "返回：returncode\n",
    "\n",
    ">p.wait()\n",
    "\n",
    "---\n",
    "\n",
    "### Popen.communicate(input=None)\n",
    "\n",
    "功能：\n",
    "\n",
    "- 等待进程结束\n",
    "\n",
    "- 与子进程进行交互。向`stdin`发送数据并关闭它。可选参数`input`指定发送到子进程的数据，注意 `Popen`对象的**`encoding`或者`text`参数**决定传入字符串还是字节流。\n",
    "\n",
    "- 从`stdout`和`stderr`中读取数据, 并关闭。\n",
    "\n",
    "如果没有数据发送到子进程（`stdin`），则返回一个元组：(stdoutdata, stderrdata)。\n",
    "\n",
    "**注意：**\n",
    "\n",
    "  **如果希望通过进程的`stdin`向其发送数据，在创建`Popen`对象的时候，参数`stdin`必须被设置为`PIPE`。同样，如果希望从`stdout`和`stderr`获取数据，必须将`stdout`和`stderr`设置为`PIPE`。**\n",
    "\n",
    "**警告：**\n",
    "\n",
    "**使用communicate（）而不是.stdin.write，.stdout.read或.stderr.read来避免由于任何其他OS管道缓冲区填满和阻止子进程而导致的死锁**\n",
    "\n",
    "---\n",
    "\n",
    "### Popen.send_signal(sig)\n",
    "\n",
    "向进程发送信号（方法内部自行检查进程是否已经终止，已经终止是不会响应信号的）\n",
    "\n",
    "```python\n",
    "import signal\n",
    "```\n",
    "两种情况的参数：\n",
    "\n",
    "Windows上：\n",
    "- 终止：signal.SIGTERM\n",
    "- Ctrl + C：signal.CTRL_C_EVENT\n",
    "- Ctrl + Break：signal.CTRL_BREAK_EVENT\n",
    "\n",
    "Linux上:\n",
    "\n",
    "- 终止：signal.SIGTERM 或者 signal.SIGKILL\n",
    "\n",
    "例如：\n",
    "\n",
    "```python\n",
    "import signal\n",
    "p.send_signal(signal.SIGTERM)\n",
    "```\n",
    "---\n",
    "\n",
    "### Popen.terminate()\n",
    "\n",
    "停止子进程。在 Posix 操作系统上，此方法发送 `SIGTERM`。在 Windows，调用 Win32 API 函数 `TerminateProcess()` 来停止子进程\n",
    "\n",
    "---\n",
    "\n",
    "### Popen.kill()\n",
    "在 Posix 操作系统上，此函数给子进程发送 `SIGKILL` 信号。在 Windows 上， `kill()` 是 `terminate()` 的别名。\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Popen的属性\n",
    "\n",
    "### Popen.stdin \n",
    "- 如果 `stdin` 参数为 `PIPE`，此属性是一个类似 `open()` 返回的**可写**的流对象。\n",
    "\n",
    "- 如果 `encoding` 或 `errors` 参数被指定或者 `universal_newlines` 参数为 `True`，则此流是一个字符串，否则是字节流。\n",
    "\n",
    "- 如果 `stdin` 参数非 `PIPE`， 此属性为 `None`。\n",
    "\n",
    "---\n",
    "\n",
    "### Popen.stdout \n",
    "- 如果 `stdin` 参数为 `PIPE`，此属性是一个类似 `open()` 返回的**可读**的流对象。\n",
    "\n",
    "- 如果 `encoding` 或 `errors` 参数被指定或者 `universal_newlines` 参数为 `True`，则此流是一个字符串，否则是字节流。\n",
    "\n",
    "- 如果 `stdin` 参数非 `PIPE`， 此属性为 `None`。\n",
    "\n",
    "---\n",
    "\n",
    "### Popen.stderr \n",
    "- 如果 `stdin` 参数为 `PIPE`，此属性是一个类似 `open()` 返回的**可读**的流对象。\n",
    "\n",
    "- 如果 `stdin` 参数为`subprocess.STDOUT` ，则错误输出到stdout。\n",
    "- 如果 `encoding` 或 `errors` 参数被指定或者 `universal_newlines` 参数为 `True`，则此流是一个字符串，否则是字节流。\n",
    "\n",
    "- 如果 `stdin` 参数非 `PIPE`， 此属性为 `None`。\n",
    "\n",
    "---\n",
    "\n",
    "### Popen.pid \n",
    "获取子进程的进程ID。\n",
    "\n",
    "---\n",
    "\n",
    "### Popen.returncode \n",
    "获取进程的返回值。如果进程还没有结束，返回None。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 常量\n",
    "\n",
    "### subprocess.PIPE\n",
    "\n",
    "在创建`Popen`对象时，`subprocess.PIPE`可以初始化`stdin, stdout`或`stderr`参数，表示与子进程通信的标准流。**如果希望从`stdout和stderr`获取数据，必须将`stdout和stderr`设置为`subprocess.PIPE`。**\n",
    "\n",
    "### subprocess.STDOUT\n",
    "创建Popen对象时，用于初始化stderr参数，表示将错误通过标准输出流输出。\n",
    "\n",
    "### subprocess.STD_INPUT_HANDLE\n",
    "\n",
    "标准输入设备，初始化时，是从console控制台输入\n",
    "\n",
    "### subprocess.STD_OUTPUT_HANDLE\n",
    "\n",
    "标准输出设备，初始化时，是活动console屏幕缓冲区\n",
    "\n",
    "### subprocess.STD_ERROR_HANDLE\n",
    "\n",
    "标准错误输出设备，初始化时，同样是console屏幕缓冲区。\n",
    "\n",
    "### subprocess.SW_HIDE\n",
    "\n",
    "隐藏窗口。另一个窗口将被激活\n",
    "\n",
    "### subprocess.CREATE_NEW_CONSOLE\n",
    "\n",
    "新进程有一个新的控制台，而不是继承其父控制台（默认）。当使用shell = True创建Popen时，始终设置此标志。          "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 基于Popen的其他方法\n",
    "\n",
    "### subprocess.call(*popenargs, timeout=None, **kwargs)\n",
    "\n",
    "说明：\n",
    "    仅能执行命令和获取执行的返回码，和超时控制，没有其他功能。不能捕获输出流。\n",
    "\n",
    "- 使用参数运行命令\n",
    "- 等待完成\n",
    "- 返回returncode属性。\n",
    "- 超时触发`TimeoutExpired` 异常\n",
    "\n",
    "参数：\n",
    "\n",
    "    同Popen构造函数\n",
    "    timeout：秒，用于等待完成，超时强制退出，释放所有资源\n",
    "\n",
    "本质：\n",
    "\n",
    "    使用上下文管理器（with 语句）：调用Popen()，以及Popen.wait(timeout)等待完成.\n",
    "    \n",
    "警告：\n",
    "\n",
    "    当输出数据较大时会发生死锁，用communicate()来避免。\n",
    "\n",
    "示例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "retcode = subprocess.call([\"ls\", \"-l\"], shell=True)\n",
    "retcode"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### subprocess.check_call(*popenargs, **kwargs)\n",
    "\n",
    "说明：\n",
    "    在上面的call()方法上加了 检查功能\n",
    "\n",
    "- 等待完成\n",
    "- 执行无误返回0，否则引发`CalledProcessError`。\n",
    "\n",
    "参数：\n",
    "\n",
    "    同subprocess.call()\n",
    "本质：\n",
    "\n",
    "    调用上面的call() 方法并在结束后检查 returncode是否为0.\n",
    "警告：\n",
    "\n",
    "    当输出数据较大时会发生死锁，用communicate()来避免。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "subprocess.check_call('calc')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "ename": "CalledProcessError",
     "evalue": "Command '['ls', '-l']' returned non-zero exit status 1.",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mCalledProcessError\u001b[0m                        Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-25-e737dd244224>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0msubprocess\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcheck_call\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m\"ls\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"-l\"\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mshell\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mTrue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32mD:\\SoftInstall\\Anacoda3\\lib\\subprocess.py\u001b[0m in \u001b[0;36mcheck_call\u001b[1;34m(*popenargs, **kwargs)\u001b[0m\n\u001b[0;32m    345\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0mcmd\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    346\u001b[0m             \u001b[0mcmd\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mpopenargs\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 347\u001b[1;33m         \u001b[1;32mraise\u001b[0m \u001b[0mCalledProcessError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mretcode\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcmd\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    348\u001b[0m     \u001b[1;32mreturn\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    349\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mCalledProcessError\u001b[0m: Command '['ls', '-l']' returned non-zero exit status 1."
     ]
    }
   ],
   "source": [
    "subprocess.check_call([\"ls\", \"-l\"], shell=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### CompletedProcess类\n",
    "（下面两个方法要用到）\n",
    "\n",
    "run（）方法的返回值\n",
    "\n",
    "属性：\n",
    "\n",
    "- args：进程执行的命令，str 或者 list\n",
    "\n",
    "- returncode：结束返回值\n",
    "\n",
    "- stdout：标准输出\n",
    "\n",
    "- stderr：标准错误\n",
    "\n",
    "方法：\n",
    "\n",
    "- check_returncode()： 检查returncode退出码，如果不是0，引发CalledProcessError异常。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### subprocess.run(*popenargs, input=None, capture_output=False, timeout=None, check=False, **kwargs):\n",
    "\n",
    "说明：\n",
    "    可从返回的CompletedProcess的实例中获取returncode、stdout、stderr属性\n",
    "\n",
    "- **等待结束**，或者超时异常\n",
    "- 超时控制\n",
    "- 结果检查\n",
    "- 进程安全，结束、异常终止都释放所占用资源\n",
    "- 在不超时，不检查returncode，或者returncode为0 的情况下返回CompletedProcess实例，否则直接异常退出\n",
    "\n",
    "参数\n",
    "\n",
    "- 与`Popen`的参数通用\n",
    "\n",
    "- `input`：向进程通信传入的数据，bytes 或者 str。如果不为None，则不能再传入 `stdin`参数\n",
    "\n",
    "- `capture_output`：如果设置为`True`，就不能再传入`stdout` 和`stderr`参数，否则报错\n",
    "\n",
    "- `timeout`：限制执行时间，超时引发`TimeoutExpired`异常。默认为一直等待到执行结束\n",
    "\n",
    "- `check` ：是否检查returncode，如果不为0，引发`CalledProcessError`异常 (可以先不检查，在返回的CompletedProcess实例中调用check_returncode方法再检查）\n",
    "\n",
    "本质：\n",
    "\n",
    "    使用Popen的with语句，和communicate()方法等待结束，并加入了超时控制和返回码检查，捕获输出更加简单，是对Popen用法的封装，功能较全。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### subprocess.check_output(*popenargs, timeout=None, **kwargs)\n",
    "\n",
    "\n",
    "说明：执行命令，当命令执行成功（returncode=0）且未超时时，只返回stdout输出。\n",
    "\n",
    "- 与上述run类似\n",
    "- 运行失败引发`CalledProcessError`异常\n",
    "- **运行成功**直接返回stdout\n",
    "\n",
    "参数：\n",
    "\n",
    "- 与run() 通用\n",
    "- 不允许设置`stdout`参数\n",
    "\n",
    "本质：\n",
    "\n",
    ">subprocess.run(...).stdout\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 疑问\n",
    "\n",
    "如下：\n",
    "\n",
    "查看java版本命令返回码为0 ，但是输出到了标准错误流中，求告知。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b''"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "subprocess.check_output(['java', '-version'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "java version \"1.8.0_202\"\n",
      "Java(TM) SE Runtime Environment (build 1.8.0_202-b08)\n",
      "Java HotSpot(TM) Client VM (build 25.202-b08, mixed mode)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "a = subprocess.run(['java', '-version'], capture_output=True, universal_newlines=True)\n",
    "print(a.stderr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('',\n",
       " 'java version \"1.8.0_202\"\\nJava(TM) SE Runtime Environment (build 1.8.0_202-b08)\\nJava HotSpot(TM) Client VM (build 25.202-b08, mixed mode)\\n')"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = subprocess.Popen(['java', '-version'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)\n",
    "a.communicate()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.returncode"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### subprocess.getstatusoutput(cmd)\n",
    "\n",
    "说明：执行命令，成功后（returncode=0）返回一个元组（返回码，**所有输出信息**）\n",
    "    \n",
    "参数：\n",
    "- cmd：执行的命令\n",
    "\n",
    "本质：\n",
    "\n",
    "   调用check_out（）方法，并设置参数`shell=True, text=True, stderr=STDOUT`\n",
    "   即将stderr重定向到标准输出，得到的是所有输出信息\n",
    "   \n",
    "得到的输出信息较完整，且调用方便，建议使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0,\n",
       " 'java version \"1.8.0_202\"\\nJava(TM) SE Runtime Environment (build 1.8.0_202-b08)\\nJava HotSpot(TM) Client VM (build 25.202-b08, mixed mode)')"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "subprocess.getstatusoutput(['java', '-version'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### subprocess.getoutput(cmd)\n",
    "\n",
    "本质：调用`subprocess.getstatusoutput`，只取输出\n",
    "\n",
    ">getstatusoutput(cmd)[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Python 3.7.3'"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "subprocess.getoutput(['python', '-V'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用举例以及注意事项\n",
    "\n",
    "- 使用communicate 等待完成并获取输出，而不用其他的等待方法，避免发生系统IO阻塞（output数据太多）\n",
    "- 参数shell=True 在所执行的命令从input() 输入的时候可能遭受shell注入攻击\n",
    "- 获取输出要与系统默认编码一致\n",
    "\n",
    "\n",
    "如果超时到期，子进程不会被杀死，所以为了正确清理一个行为良好的应用程序应该杀死子进程并完成通讯\n",
    "\n",
    "```python\n",
    "proc = subprocess.Popen(...)\n",
    "try:\n",
    "    outs, errs = proc.communicate(timeout=15)\n",
    "except TimeoutExpired:\n",
    "    proc.kill()\n",
    "    outs, errs = proc.communicate()\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " 在POSIX上建议cmd命令传入一个序列，而不是字符串，尤其是在复杂的情形下。\n",
    " \n",
    " 可以用shellx.split()，方法将字符串安全正确的转换为一个列表序列：\n",
    "```python\n",
    ">>> import shlex, subprocess\n",
    ">>> command_line = input()\n",
    "/bin/vikings -input eggs.txt -output \"spam spam.txt\" -cmd \"echo '$MONEY'\"\n",
    ">>> args = shlex.split(command_line)\n",
    ">>> print(args)\n",
    "['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', \"echo '$MONEY'\"]\n",
    ">>> p = subprocess.Popen(args) # Success!\n",
    "```\n",
    "\n",
    "**在windows上序列会先转为字符串再执行**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 修改linux账户密码（输入操作，注意使用回车\\n）\n",
    "\n",
    "```python\n",
    "subprocess.run('sudo passwd root', input=b'123\\n123', shell =True)\n",
    "```\n",
    ">输入新的 UNIX 密码：重新输入新的 UNIX 密码：passwd：已成功更新密码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 执行结果保存在文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "cmd = 'ping www.baidu.com'  \n",
    "fhandle = open(\"aa.txt\", \"w\")  \n",
    "pipe = subprocess.Popen(cmd, shell=True, stdout=fhandle)\n",
    "fhandle.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 执行结果使用管道输出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'Python 3.7.3\\r\\n'"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output = subprocess.Popen([\"python\", \"-V\"], stdout=subprocess.PIPE).communicate()[0]\n",
    "output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "正在 Ping www.a.shifen.com [180.101.49.11] 具有 32 字节的数据:\n",
      "来自 180.101.49.11 的回复: 字节=32 时间=97ms TTL=53\n",
      "来自 180.101.49.11 的回复: 字节=32 时间=116ms TTL=53\n",
      "来自 180.101.49.11 的回复: 字节=32 时间=145ms TTL=53\n",
      "来自 180.101.49.11 的回复: 字节=32 时间=261ms TTL=53\n",
      "\n",
      "180.101.49.11 的 Ping 统计信息:\n",
      "    数据包: 已发送 = 4，已接收 = 4，丢失 = 0 (0% 丢失)，\n",
      "往返行程的估计时间(以毫秒为单位):\n",
      "    最短 = 97ms，最长 = 261ms，平均 = 154ms\n",
      "\n"
     ]
    }
   ],
   "source": [
    "pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, encoding='gbk').stdout\n",
    "print(pipe.read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 代替shell管道符\n",
    "\n",
    "查看python进程\n",
    "```shell\n",
    "ps -ef |grep python\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p1 = subprocess.Popen([\"ps\", \"-ef\"], stdout=subprocess.PIPE)\n",
    "p2 = subprocess.Popen([\"grep\", \"hda\"], stdin=p1.stdout, stdout=subprocess.PIPE)\n",
    "p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.\n",
    "output = p2.communicate()[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 选择执行程序\n",
    "\n",
    "在windows上使用Git 的执行shell命令\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('\\nD:\\\\OneDrive - business\\\\jupyter notebook\\\\Modules-Learn>', '')"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "exe_path = r'D:\\SoftInstall\\Git\\git-cmd.exe'\n",
    "\n",
    "p = subprocess.Popen('ifconfig',\n",
    "                     executable=exe_path,\n",
    "                     shell=True,\n",
    "                     stdout=subprocess.PIPE,\n",
    "                     stderr=subprocess.PIPE,\n",
    "                     universal_newlines=True)\n",
    "\n",
    "p.communicate()\n",
    "# 好像没有什么卵用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 环境变量\n",
    "\n",
    "```python\n",
    "subprocess.Popen([\"/bin/mycmd\", \"myarg\"], env={\"PATH\": \"/usr/bin\"})\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用总结\n",
    "\n",
    "- 仅获取所有输出，不管命令执行是否成功，不加任何异常控制：用getstatusoutput、getoutput\n",
    "\n",
    "- 仅执行命令，其他控制逻辑全凭自己控制：用Popen及其实例方法\n",
    "\n",
    "- 仅保证命令成功执行：check_call\n",
    "\n",
    "- 在保证命令成功执行后，仅获取stdout输出：check_output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "542.4px",
    "left": "40px",
    "top": "300.8px",
    "width": "307.2px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
